﻿using Microsoft.Xna.Framework;

namespace Microsoft.Goldenlight.Framework.Scene
{
    /// <summary>
    /// 	Object which provides basic functionality for 2D game objects.
    /// </summary>
    /// <remarks>
    /// 	SceneObjects can be added to a scene graph, can be mounted to other scene objects, and can be modified by adding a number of built in behaviors. In particular, to add collision checking add a Collision2DBehavior, to add physics add a Physics2DBehavior, and to add the ability to hold link points add a LinkPointBehavior.
    /// </remarks>
    public class SceneObject2D : GameObject
    {
        #region Fields & Properties

        protected SceneGraph2D _sceneGraph;

        /// <summary>
        /// </summary>
        public SceneGraph2D SceneGraph
        {
            get
            {
                return _sceneGraph;
            }
            set
            {
            }
        }

        protected BoundingRectangle _clipRectangle;

        /// <summary>
        /// 	World-space rectangle bounding our object.
        /// </summary>
        public BoundingRectangle ClipRectangle
        {
            get
            {
                return _clipRectangle;
            }
        }

        /// <summary>
        /// 	World-space rectangle bounding our object for collision.
        /// </summary>
        public BoundingRectangle CollisionClipRectangle
        {
            get
            {
                return _clipRectangle;
            }
        }

        #endregion

        #region Layer Fields & Properties

        protected int _layer;

        /// <summary>
        /// 	Layer to draw scene object on. Lower numbered layers are drawn on top.
        /// </summary>
        public int Layer
        {
            get
            {
                return _layer;
            }
            set
            {
                _layer = value;
            }
        }

        /// <summary>
        /// 	LayerMask is used for collision checking. The layer mask is determined by setting the bit corresponding with the layer (layers outside the range of 0..31 are treated as Layer % 32).
        /// </summary>
        public uint LayerMask
        {
            get
            {
                return (uint) 1 << _layer;
            }
        }

        /// <summary>
        /// 	Order of object within a layer. This is a read-only property used when rendering to make sure objects are rendered in the same order from frame to frame.
        /// </summary>
        public int LayerOrder
        {
            get
            {
                return (int) Id;
            }
        }

        protected float _layerDepth;

        /// <summary>
        /// 	Object is drawn at this z coordinate when rendering. This is a function of Layer and is used for rendering purposes only.
        /// </summary>
        [CloneIgnore]
        public float LayerDepth
        {
            get
            {
                return _layerDepth;
            }
            set
            {
                _layerDepth = value;
            }
        }

        protected Vector2 _sortPoint;

        /// <summary>
        /// 	The reference point for sorting a scene objects within a layer.
        /// </summary>
        public virtual Vector2 SortPoint
        {
            get
            {
                return _sortPoint;
            }
            set
            {
                _sortPoint = value;
            }
        }

        #endregion

        #region Constructors

        #endregion

        #region Spatial Operations

        #region Fields & Properties

        protected bool _visible = true;

        /// <summary>
        /// 	True if object should be rendered. Collisions are also disabled if not visible.
        /// </summary>
        public bool Visible
        {
            get
            {
                if (this.InheritMountVisibility && this.IsMounted)
                {
                    return this.MountedTo.Visible;
                }
                return _visible;
            }
            set
            {
                _visible = value;
                _spatialDirty = true;
            }
        }

        protected float _visibilityLevel;

        /// <summary>
        /// 	Sets alpha blend value to control the degree of visibility. If Visible property is false then object is not visible no matter what this value is.
        /// </summary>
        public float VisibilityLevel
        {
            get
            {
                if (this.InheritMountVisibility && this.IsMounted)
                {
                    return this.MountedTo.VisibilityLevel;
                }
                return _visibilityLevel;
            }
            set
            {
                _visibilityLevel = value;
            }
        }

        protected Vector2 _size;

        /// <summary>
        /// 	Size in world units of scene object.
        /// </summary>
        public virtual Vector2 Size
        {
            get
            {
                return _size;
            }
            set
            {
                if (_size != value)
                {
                    _size = value;
                    _spatialDirty = true;
                    this.UpdateSpatialData();
                    if (this.Collision != null)
                    {
                        this.Collision.MarkCollisionDirty();
                    }
                }
            }
        }

        protected Vector2 _position;

        /// <summary>
        /// 	Position of scene object.
        /// </summary>
        /// <remarks>
        /// 	Setting this property after the object has been registered will cause it to interpolate between current position and new position. In order to make an object move to a new position without interpolation use WarpToPosition method.
        /// </remarks>
        public virtual Vector2 Position
        {
            get
            {
                return _position;
            }
            set
            {
                _position = value;
                this._postTick.pos = _position;
                _spatialDirty = true;
                this.UpdateSpatialData();
            }
        }

        protected float _rotation;

        /// <summary>
        /// 	Rotation of scene object.
        /// </summary>
        /// <remarks>
        /// 	Setting this property after the object has been registered will cause it to interpolate between current rotation and new rotation. In order to make an object move to a new rotation without interpolation use WarpToPosition method.
        /// </remarks>
        public virtual float Rotation
        {
            get
            {
                return _rotation;
            }
            set
            {
                _rotation = (value % 360f + 360f) % 360f;
                this._postTick.rot = _rotation;
                _spatialDirty = true;
                this.UpdateSpatialData();
            }
        }

        protected bool _spatialDirty;

        /// <summary>
        /// </summary>
        [CloneIgnore]
        public bool SpatialDirty
        {
            get
            {
                return _spatialDirty;
            }
        }

        #endregion

        #endregion

        #region Mount

        #endregion
    }
}